#include "BuddyTreeList.h"

#include <iostream>

#include "wx/setup.h"
#include "../../core/Group.h"
#include "../../core/Impp.h"
#include "SearchField.h"
//#include <gtk/gtk.h>


namespace Impp {

BEGIN_EVENT_TABLE(BuddyTreeList, wxScrolledWindow)
	EVT_PAINT(BuddyTreeList::onPaint)
	EVT_LEFT_DOWN(BuddyTreeList::onMouseDown)
	EVT_RIGHT_UP(BuddyTreeList::onMouseDown)
	EVT_LEFT_DCLICK(BuddyTreeList::onMouseDown)
	EVT_ERASE_BACKGROUND(BuddyTreeList::OnEraseBackground)
	EVT_MOTION(BuddyTreeList::onMouseMoved)
	EVT_ENTER_WINDOW(BuddyTreeList::onMouseMoved)
	EVT_LEAVE_WINDOW(BuddyTreeList::onMouseMoved)
	EVT_KEY_DOWN(BuddyTreeList::onKeyDown)
END_EVENT_TABLE()

BuddyTreeList::BuddyTreeList(wxPanel* parent, long id) :
	wxScrolledWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE|wxTAB_TRAVERSAL|wxWANTS_CHARS),
	selected(NULL),indent(16),showOffline(false),useGroups(false),showAvas(false),shownPriority(BUDDY_PRIORITY_LOW)
{
	Impp* impp = Impp::exemplar();

	this->rootWithGroups = new GroupTreeItem(impp->getRootGroup());
	this->rootNoGroups = new GroupTreeItem(impp->getRootGroup());
	this->offlineBuddies = new GroupTreeItem(new Group(0,wxT("Offline Buddies"), GROUP_NOFLAG));

	dbModule = impp->getDatabase()->getModule(wxT("BuddyList"));

	this->showOffline = dbModule.getIntValue(BUDDYLIST_DB_BUDDY_SHOW_OFFLINE,0);
	this->useGroups = dbModule.getIntValue(BUDDYLIST_DB_GROUPS_USE_GROUPS,1);
	this->showAvas = dbModule.getIntValue(BUDDYLIST_DB_BUDDY_SHOW_AVAS,1);
	this->hideEmptyGroups = dbModule.getIntValue(BUDDYLIST_DB_GROUPS_HIDE_EMPTY,0);
	this->treatUnknownAsOffline = dbModule.getIntValue(BUDDYLIST_DB_BUDDY_TREAT_UNKNOWN_AS_OFFLINE,1);
	this->dimIdle = dbModule.getIntValue(BUDDYLIST_DB_BUDDY_DIM_IDLE,1);
	this->shownPriority = (BuddyPriority) dbModule.getIntValue(BUDDYLIST_DB_BUDDY_SHOWN_PRIORITY, BUDDY_PRIORITY_LOW);;

	wxFont systemFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
	buddyFont = systemFont;
	groupFont = systemFont;
	groupFont.SetWeight(wxBOLD);

    //update Buddy Font Size
    if(!showAvas){
    	buddySize.y = 18;
    }else{
    	buddySize.y = 34;

    }

	#ifdef __WIN32__
		buddyFont.SetPointSize(10);
		groupFont.SetPointSize(10);
	#else
		buddyFont.SetPointSize(12);
		groupFont.SetPointSize(12);
	#endif

    groupSize.y = 18;



	textColor = *wxBLACK;
	textColorHighlight = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);

	bg = *wxWHITE_BRUSH;
	bgHighlight = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);

	iUserInterface* ui = Impp::exemplar()->getModuleManager()->getUserInterfaceModule();
	wxASSERT(ui != NULL);

	groupIsExpanded = ui->getIcons()->getBitmap(wxT("icons/buddylist/expand_is.png"));
	groupNotExpanded = ui->getIcons()->getBitmap(wxT("icons/buddylist/expand_not.png"));


	toolTip = new EntryToolTip(this);

	SetFocus();

    std::vector<Group*> groups = impp->getAllGroups();
    for (size_t i = 0; i < groups.size(); ++i) {
    	addGroup(groups[i]);
	}

    std::vector<Buddy*> buddies = impp->getAllBuddies();
    for (size_t i = 0; i < buddies.size(); ++i) {
    	addBuddy(buddies[i], buddies[i]->getGroup());
	}


    impp->Connect(EVT_BUDDY_STATUS_CHANGED,StatusEventHandler(BuddyTreeList::onBuddyStatusChanged), NULL, this);
    impp->Connect(EVT_BUDDY_AVATAR_CHANGED,AvatarChangedEventHandler(BuddyTreeList::onAvatarChanged), NULL, this);
    impp->Connect(EVT_MESSAGE_IN,MessageEventHandler(BuddyTreeList::onMessageIn), NULL, this);
    impp->Connect(EVT_MESSAGE_READ,MessageReadEventHandler(BuddyTreeList::onMessageRead), NULL, this);
    impp->Connect(EVT_BUDDY_CHANGED,BuddyChangedEventHandler(BuddyTreeList::onBuddyChanged), NULL, this);
    impp->Connect(EVT_GROUP_CHANGED,GroupChangedEventHandler(BuddyTreeList::onGroupChanged), NULL, this);
}

BuddyTreeList::~BuddyTreeList()
{
	Group* offlineGroup = offlineBuddies->getGroup();
    wxDELETE(offlineGroup);
    wxDELETE(offlineBuddies);
    wxDELETE(rootWithGroups);
    wxDELETE(rootNoGroups);
}

void BuddyTreeList::updateSize(){
	//clear the last displayed Items, we dont need them we are
	//making a new list
	displayedItems.clear();

	//the item position will be increased by claculateSize
	//and will be at the end the deepest point in our buddylist
	//(last buddypos + lastbuddyhight)
	wxPoint itemPos = startPos;

	if(useGroups){
		calculateSize(rootWithGroups,itemPos);
	}else{
		calculateSize(rootNoGroups,itemPos);
	}

	//As the scrollar units will be 10 we have to
	//round the high for the scrollbars to next 10
	int height= itemPos.y + (itemPos.y%10!=0 ? 10-(itemPos.y%10) : 0);

	fullSize.SetHeight(height);

	//if the high now differes we have to update scollbars
	if(lastFullSize.GetHeight()!=fullSize.GetHeight()){
		wxPoint viewStart;
		GetViewStart(&viewStart.x,&viewStart.y);

		SetScrollbars(0,10,0,height/10);
		Scroll(viewStart.x,viewStart.y);

		lastFullSize = fullSize;
	}

	fullSize.SetWidth(GetClientSize().x);

}


void BuddyTreeList::setShowOffline(bool show){
	if(showOffline==show){
		return;
	}
	showOffline=show;
	dbModule.setIntValue(BUDDYLIST_DB_BUDDY_SHOW_OFFLINE,showOffline);

	if(showOffline){
		std::vector<TreeItem*> offline = offlineBuddies->getChildren();
		std::vector<TreeItem*>::iterator i(offline.begin());

		while(i!=offline.end()){
			if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(*i)){
				offlineBuddies->removeChild(bti);
				addBuddyTreeItemToRightGroup(bti);
			}else{
				wxLogDebug(wxT("BuddyTreeList::setShowOffline: A Group in offlineBuddies???"));
			}
			i++;
		}
	}else{
		if(useGroups){
			offlineBuddiesToOfflineGroup(rootWithGroups);
		}else{
			offlineBuddiesToOfflineGroup(rootNoGroups);
		}
	}
	Refresh();
}

void BuddyTreeList::setUseGroups(bool use){
	if(useGroups==use){
		return;
	}
	useGroups=use;
	dbModule.setIntValue(BUDDYLIST_DB_GROUPS_USE_GROUPS,use);
	if(useGroups){
		std::vector<TreeItem*> noGroups = rootNoGroups->getChildren();
		std::vector<TreeItem*>::iterator i(noGroups.begin());

		while(i!=noGroups.end()){
			if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(*i)){
				rootNoGroups->removeChild(bti);
				addBuddyTreeItemToRightGroup(bti);
			}else{
				if(GroupTreeItem* gti = dynamic_cast<GroupTreeItem*>(*i)){
					wxLogDebug(wxT("BuddyTreeList::setUseGroups: A Group in rootNoGroups??? %s"),gti->getGroup()->getName().c_str());
				}else{
					wxLogDebug(wxT("BuddyTreeList::setUseGroups: There is something else in the group..."));
				}

			}
			i++;
		}
	}else{
		buddiesToRootNoGroup(rootWithGroups);
		rootNoGroups->sort();
	}

	Refresh();
}

void BuddyTreeList::setShowAvas(bool show){
	if(showAvas==show){
		return;
	}
	dbModule.setIntValue(BUDDYLIST_DB_BUDDY_SHOW_AVAS,show);
	showAvas=show;

    //update Buddy Font Size
    if(!showAvas){
    	buddySize.y = 18;
    }else{
    	buddySize.y = 34;
    }

	Refresh();
}

void BuddyTreeList::setDimIdle(bool dim){
	dbModule.setIntValue(BUDDYLIST_DB_BUDDY_DIM_IDLE,dim);
	if(dimIdle == dim){
		return;
	}
	dimIdle=dim;

	Refresh();
}

void BuddyTreeList::setShowBuddiesWithPriority(BuddyPriority priority){

	if(shownPriority == priority){
		return;
	}
	shownPriority = priority;
	dbModule.setIntValue(BUDDYLIST_DB_BUDDY_SHOWN_PRIORITY,priority);
	Refresh(false);
}

void BuddyTreeList::offlineBuddiesToOfflineGroup(GroupTreeItem* gti){
	std::vector<TreeItem*> items = gti->getChildren();
    for(size_t i=0; i <  items.size(); i++ ){
    	if(GroupTreeItem* gti2 = dynamic_cast<GroupTreeItem*>(items[i])){
    		offlineBuddiesToOfflineGroup(gti2);
    	}else if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(items[i])){
    		if(treatAsOffline(bti->getBuddy()->getStatus())){
    			gti->removeChild(bti);
    			offlineBuddies->addChild(bti);
    		}
    	}
    }
}

void BuddyTreeList::buddiesToRootNoGroup(GroupTreeItem* gti){
	std::vector<TreeItem*> items = gti->getChildren();
    for(size_t i=0; i <  items.size(); i++ ){
    	if(GroupTreeItem* gti2 = dynamic_cast<GroupTreeItem*>(items[i])){
    		buddiesToRootNoGroup(gti2);
    	}else if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(items[i])){
    		gti->removeChild(bti);
    		rootNoGroups->addChild(bti);
    	}
    }
}

void BuddyTreeList::onPaint(wxPaintEvent& evt)
{
	//prepare the dc:
    wxBufferedPaintDC bpdc(this);
    wxGCDC dc(bpdc);

    DoPrepareDC(dc);


    //Skip Main Group label:
    startPos.x=-indent;
    startPos.y=-groupSize.GetHeight();

	//so lets update the size and the buddies in the displayed list
	//so wie can draw them later.
	updateSize();

    dc.SetBrush(*wxWHITE_BRUSH);
    dc.SetPen(*wxWHITE_PEN);

    //draw the background rect in white:
	wxPoint scrollOffset;
	GetViewStart(&scrollOffset.x,&scrollOffset.y);
	wxPoint tmp;
	GetScrollPixelsPerUnit(&tmp.x,&tmp.y);

    dc.DrawRectangle(wxPoint(0,0),GetSize()+wxSize(0,scrollOffset.y*tmp.y));

    //update the width of the buddies and groups
    groupSize.x = fullSize.GetWidth();
    buddySize.x = fullSize.GetWidth();

    // get the update rect list
    wxRegionIterator upd(GetUpdateRegion());
    //iterate throw all dirty Regions:

    dc.SetPen(*wxTRANSPARENT_PEN);

    while(upd){
    	wxRect updateRegion = upd.GetRect();
		//so now finaly draw the buddies in displayed list:
		std::vector<TreeItemPosition>::iterator i(displayedItems.begin());
		for (; i != displayedItems.end(); ++i) {
			const TreeItemPosition& item = *i;
			//calculate the scrolled pos to check below if the buddie is visisble:
			wxPoint itemScrolledPos = CalcScrolledPosition(item.pos);

			//check what item we hava to drawing:
			if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(item.item)){
				//Item was a Buddy so draw him if visible:
				if(itemScrolledPos.y >= updateRegion.y-buddySize.y && itemScrolledPos.y < updateRegion.y+updateRegion.height){
					paintBuddyTreeItem(dc,bti,item.pos);
				}
			}else if(GroupTreeItem* gti = dynamic_cast<GroupTreeItem*>(item.item)){
				//Item was a Group so draw the group if it is visible:
				if(itemScrolledPos.y >= updateRegion.y-groupSize.y && itemScrolledPos.y < updateRegion.y+updateRegion.height){
					paintGroupTreeItem(dc,gti,item.pos);
				}
			}else{
				//we have just Buddies and Groups so this shoud never happen
				//if so check for a NULL Pointer
				wxASSERT(item.item != NULL);
				wxLogDebug(wxT("BuddyTreeList::onPaint: Illegal Item in displayList!"));
			}
		}
        upd++ ;
    }
}

void BuddyTreeList::paintBuddyTreeItem(wxGCDC & dc,BuddyTreeItem* bti,const wxPoint& pos){

	dc.SetFont(buddyFont);

	if(bti==selected){
		dc.SetBrush(bgHighlight);
		dc.DrawRectangle(wxPoint(0,pos.y),buddySize);
		dc.SetTextForeground(textColorHighlight);
	}else{
		dc.SetBrush(bg);
		dc.SetTextForeground(textColor);
	}

	bti->paint(dc,pos,buddySize - wxSize(pos.x,0),showAvas,dimIdle);

}


void BuddyTreeList::paintGroupTreeItem(wxGCDC & dc,GroupTreeItem* gti,const wxPoint& pos){

	dc.SetFont(groupFont);
	if(gti==selected){
		dc.SetBrush(bgHighlight);
		dc.DrawRectangle(wxPoint(0,pos.y),groupSize);
		dc.SetTextForeground(textColorHighlight);
	}else{
		dc.SetTextForeground(textColor);
	}

	size_t hiddenCount = 0;
	if(shownPriority != BUDDY_PRIORITY_LOW){

		std::vector<TreeItem*> children = gti->getChildren();
		std::vector<TreeItem*>::iterator i(children.begin());
		while(i != children.end()){
			if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(*i)){
				if(!shouldDraw(bti)){
					hiddenCount++;
				}
			}
			i++;
		}
	}


	gti->paint(dc,pos,groupSize, shownPriority, hiddenCount, this);
}

void BuddyTreeList::calculateSize(GroupTreeItem* gti, wxPoint & pos){
	//check if we shoud draw this group:
	if(shouldDraw(gti)){
		//Ok so wie have to draw this group so add it to the list
		//that will be drawn and update the size:
		displayedItems.push_back(TreeItemPosition(gti,pos));
		pos.y+=groupSize.GetHeight();
		//So check if the group is extended, if it is, we draw the cildren:
		if(gti->isExtended()){
			//all sub items will be more on the right:
			pos.x+=indent;
			//Ok for all sub items add them to drawlist:
			std::vector<TreeItem*> items = gti->getChildren();
		    for(size_t i=0; i <  items.size(); i++ ){
		    	if(GroupTreeItem* gti2 = dynamic_cast<GroupTreeItem*>(items[i])){
		    		//do the same for the sub group:
			    	calculateSize(gti2,pos);
		    	}else if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(items[i])){
		    		if(shouldDraw(bti)){
			    		//add the Buddy to the display list:
			    		displayedItems.push_back(TreeItemPosition(bti,pos));
			    		pos.y+=buddySize.GetHeight();
		    		}
		    	}
		    }
		    //we have drawn the sub items so go to the left:
		    pos.x-=indent;
		}
	}
}

void BuddyTreeList::onMouseDown(wxMouseEvent& event) {
	SetFocus();
	SetFocusFromKbd();

	wxPoint pos = event.GetPosition();
	wxPoint loc;
	TreeItem* ti = getTreeItem(pos,loc);
	if(ti){
		setSelected(ti);
    	if(GroupTreeItem* gti = dynamic_cast<GroupTreeItem*>(ti)){
    		if(event.LeftDClick()){
    			setGroupExpended(gti,!gti->isExtended());
    		}
    	}
    	if(BuddyTreeItem* bi = dynamic_cast<BuddyTreeItem*>(ti)){
    		if(event.LeftDClick()){
    			iChatWindow* chatWin = Impp::exemplar()->getModuleManager()->getChatWindowModule();
    			if(chatWin != NULL){
    				chatWin->showChatWindow(bi->getBuddy());
    			}else{
    				wxLogDebug(wxT("BuddyTreeList::onMouseDown: No Chatwindow that can be showen!"));
    			}
    		}else if(event.RightUp()){

    			toolTip->hide();

    			iUserInterface* ui = Impp::exemplar()->getModuleManager()->getUserInterfaceModule();
    			if(ui != NULL){
    				wxMenu menu;
        			ui->buildBuddyMenu(&menu, bi->getBuddy());
        			PopupMenu(&menu,event.GetPosition());
    			}else{
    				wxLogDebug(wxT("BuddyTreeList::onMouseDown: No UserInterface found!"));
    			}




    		}
    	}

    }else{
		//std::cout << "null" << std::endl;
	}
	event.Skip(true);

}

void BuddyTreeList::setSelected(TreeItem* item){
	if(selected!=NULL){
		refreshTreeItem(selected);
	}
	selected=item;
	if(selected!=NULL){
		refreshTreeItem(selected);
	}
}

void BuddyTreeList::refreshTreeItem(TreeItem* item){
	std::vector<TreeItemPosition>::iterator i(displayedItems.begin());
	for (; i != displayedItems.end(); ++i) {
		TreeItem* curItem = i->item;
		if(curItem == item){
			if(dynamic_cast<BuddyTreeItem*>(item)){
				wxPoint scrolledPos = CalcScrolledPosition(i->pos);
				wxRect rect(0, scrolledPos.y, buddySize.x, buddySize.y);
				RefreshRect(rect);
				return;
			}else if(dynamic_cast<GroupTreeItem*>(item)){
				wxPoint scrolledPos = CalcScrolledPosition(i->pos);
				wxRect rect(0, scrolledPos.y, groupSize.x, groupSize.y);
				RefreshRect(rect);
				return;
			}
		}
	}
}

void  BuddyTreeList::onMouseMoved(wxMouseEvent& event){

	wxPoint pos = event.GetPosition();
	wxPoint loc;
	TreeItem* ti = getTreeItem(pos,loc);
	BuddyTreeItem* bi = NULL;

	if(ti && (bi = dynamic_cast<BuddyTreeItem*>(ti))){
		wxCoord width = GetScreenPosition().x+GetSize().GetWidth();
		wxSize screenSize = wxGetDisplaySize();

		loc.x = width+toolTip->GetSize().GetWidth() > screenSize.GetWidth() ? GetScreenPosition().x - toolTip->GetSize().GetWidth() : width;
		loc.y+= GetScreenPosition().y;
    }else{
		//std::cout << "null" << std::endl;
		toolTip->hide();
	}

	wxEventType type = event.GetEventType();

	if(type == wxEVT_ENTER_WINDOW){
		if(bi){
			toolTip->trigger(bi->getBuddy(),loc);
		}
	}else if(type == wxEVT_LEAVE_WINDOW){
		toolTip->hide();
	}else if(type == wxEVT_MOTION){
		if(bi){
			toolTip->trigger(bi->getBuddy(),loc);
		}
	}
	event.Skip(true);
}

TreeItem* BuddyTreeList::getTreeItem(wxPoint& pos, wxPoint& itemPos){
	wxPoint scrollOffset;
	GetViewStart(&scrollOffset.x,&scrollOffset.y);
	wxPoint tmp;
	GetScrollPixelsPerUnit(&tmp.x,&tmp.y);
	wxPoint spos = wxPoint(startPos.x-scrollOffset.x*tmp.x,startPos.y-scrollOffset.y*tmp.y);

	itemPos = spos;


	TreeItem* ti = NULL;
	if(useGroups){
		ti = getTreeItem(rootWithGroups,pos,itemPos);
	}else{
		ti = getTreeItem(rootNoGroups,pos,itemPos);
	}
	return ti;
}

void BuddyTreeList::setGroupExpended(GroupTreeItem* gti, bool extended){
	gti->setExtended(extended);
	Refresh(false);
}

void BuddyTreeList:: setHideEmptyGroups(bool hide){

	if(hideEmptyGroups==hide){
		return;
	}
	hideEmptyGroups=hide;
	dbModule.setIntValue(wxT("hideEmptyGroups"),hide);
	Refresh(false);
}

TreeItem* BuddyTreeList::getTreeItem(GroupTreeItem* gti, wxPoint& pos, wxPoint& startPoint){

	if(shouldDraw(gti)){
		if(pos.y >= startPoint.y && pos.y < startPoint.y + groupSize.GetHeight() ){
			return gti;
		}

		startPoint.y+=groupSize.GetHeight();

		if(gti->isExtended()){
			startPoint.x+=indent;

			std::vector<TreeItem*> items = gti->getChildren();

		    for(size_t i=0; i <  items.size(); i++ ){
		    	if(GroupTreeItem* gti2 = dynamic_cast<GroupTreeItem*>(items[i])){
		    		TreeItem* ret = getTreeItem(gti2, pos, startPoint);
		    		if(ret){
		    			return ret;
		    		}
		    	}else if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(items[i])){
		    		if(shouldDraw(bti)){
			    		if(pos.y >= startPoint.y && pos.y < startPoint.y + buddySize.GetHeight() ){
			    			return bti;
			    		}
			    		startPoint.y+=buddySize.GetHeight();
		    		}
		    	}
		    }
		}
	    startPoint.x-=indent;
	}
    return NULL;
}

void BuddyTreeList::addGroup(Group* group){
	GroupTreeItem* gte =new GroupTreeItem(group);
	rootWithGroups->addChild(gte);
	Refresh(false);
}

void BuddyTreeList::addBuddy(Buddy* buddy, Group* subGroup){
	BuddyTreeItem* bte =new BuddyTreeItem(buddy);
	addBuddyTreeItemToRightGroup(bte);
	Refresh(false);
}

void BuddyTreeList::removeBuddy(Buddy* buddy){
	bool succes = false;

	if(useGroups){
		succes = removeBuddy(buddy,rootWithGroups);
		if(succes){
			Refresh();
			return;
		}
	}else{
		succes = removeBuddy(buddy,rootNoGroups);
		if(succes){
			Refresh();
			return;
		}
	}
	succes = removeBuddy(buddy, offlineBuddies);
	Refresh();
	if(!succes){
		wxLogError(wxT("BuddyTreeList::removeBuddy Coud not remove Buddy, because Buddy was not found!"));
	}
}

bool BuddyTreeList::removeBuddy(Buddy* buddy, GroupTreeItem* gti){
	std::vector<TreeItem*> items = gti->getChildren();

    for(size_t i=0; i <  items.size(); i++ ){
    	if(GroupTreeItem* gti2 = dynamic_cast<GroupTreeItem*>(items[i])){
    		bool succes = removeBuddy(buddy,gti2);
    		if(succes){
    			return succes;
    		}
    	}else if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(items[i])){
    		if(bti->getBuddy() == buddy){
    			gti->removeChild(bti);
    			delete bti;
    			if(bti == selected){
    				selected = NULL;
    			}
    			return true;
    		}
    	}
    }
    return false;
}

void BuddyTreeList::refreshBuddy(Buddy* buddy){
	std::vector<TreeItemPosition>::iterator i(displayedItems.begin());
	for (; i != displayedItems.end(); ++i) {
		if(BuddyTreeItem* bti = dynamic_cast<BuddyTreeItem*>(i->item)){
			if(bti->getBuddy() == buddy){
				wxPoint scrolledPos = CalcScrolledPosition(i->pos);
				wxRect rect(0, scrolledPos.y, buddySize.x, buddySize.y);
				RefreshRect(rect);
				return;
			}
		}
	}
}

wxBitmap BuddyTreeList::getExpandedIcon(bool extended){
	if(extended){
		return groupIsExpanded;
	}else{
		return groupNotExpanded;
	}
}

void BuddyTreeList::addBuddyTreeItemToRightGroup(BuddyTreeItem* bti){
	wxASSERT(bti != NULL);
	wxLogTrace(wxT("BUDDYLIST"),wxT("BuddyTreeList::addBuddyTreeItemToRightGroup called"));
	Buddy* buddy = bti->getBuddy();
	Group* subGroup = buddy->getGroup();

	if(showOffline || !treatAsOffline(buddy->getStatus())){
		if(useGroups){
			GroupTreeItem* subGroupItem = getGroupTreeItem(subGroup,rootWithGroups);
			if(subGroupItem){
				subGroupItem->addChild(bti);
				subGroupItem->sort();
			}else{
				delete bti;
				wxLogError(wxT("Coudn't add Buddy, subGroup not in the tree"));
			}
		}else{
			rootNoGroups->addChild(bti);
			rootNoGroups->sort();
		}
	}else{
		offlineBuddies->addChild(bti);
	}

}



GroupTreeItem* BuddyTreeList::getGroupTreeItem(Group* g, GroupTreeItem* gti){
	Group* cur = gti->getGroup();
	if(g==cur){
		return gti;
	}

	std::vector<TreeItem*> items = gti->getChildren();

    for(size_t i=0; i <  items.size(); i++ ){
    	if(GroupTreeItem* gti2 = dynamic_cast<GroupTreeItem*>(items[i])){
    		GroupTreeItem* ret = getGroupTreeItem(g,gti2);
    		if(ret){
    			return ret;
    		}
    	}
    }
	return NULL;

}

void BuddyTreeList::onBuddyStatusChanged(StatusEvent & event){
	event.Skip();
	wxLogTrace(wxT("BUDDYLIST"),wxT("BuddyTreeList::buddyStatusChanged called"));

	//the Group item of the Buddy
	GroupTreeItem* gti = NULL;

	//check if we have to keep the Buddy in its GroupTreeItem and just sort the Group
	if(showOffline || (!treatAsOffline(event.getOldStatus()) && !treatAsOffline(event.getNewStatus()))){
		//showOffline = true so wie have to look in the normal group
		//Or the old was not OFFLINE and the new Status ist not Offline
		if(useGroups){
			//if we use groups, wie have to look for the GroupTreeItem in the rootWithGroups
			gti = getGroupTreeItem(event.getBuddy()->getGroup(),rootWithGroups);
		}else{
			//very simply because we have just one GroupTreeItem
			gti = rootNoGroups;
		}
		if(gti){
			//we have just to sort the GroupTreeItem
			gti->sort();

		}else{
			wxLogError(wxT("BuddyTreeList::buddyStatusChanged: (showOffline | old and new Status not Offline) Buddy %s (%s) not found: oldStatus: %s newStatus: %s"),event.getBuddy()->getCustomName().c_str(),event.getBuddy()->getScreenName().c_str(),statusToString(event.getOldStatus()).c_str(),statusToString(event.getNewStatus()).c_str());
		}
	//so we have to chenge the GroupTreeItem of the Buddy
	}else{
		//the new Status is Offline and the old one was an online status
		if(treatAsOffline(event.getNewStatus())){
			//get the right gti
			if(useGroups){
				gti = getGroupTreeItem(event.getBuddy()->getGroup(),rootWithGroups);
			}else{
				gti = rootNoGroups;
			}
			if(gti){
				BuddyTreeItem* bti = gti->getChild(event.getBuddy());
				if(bti == NULL){
					//OK Buddy was not found so mybe he is already in offlineBuddies:
					bti = offlineBuddies->getChild(event.getBuddy());
					if(bti != NULL){
						//Yeah found him, so nothing to do
						;
					}else{
						wxLogError(wxT("BuddyTreeList::buddyStatusChanged: (new Status Offline) Buddy %s (%s) not found, newStatus: OFFLINE"),event.getBuddy()->getCustomName().c_str(),event.getBuddy()->getScreenName().c_str());
					}
				}else{
					//we have found him in his group so move him to offline!
					gti->removeChild(bti);
					offlineBuddies->addChild(bti);
					wxLogTrace(wxT("BUDDYLIST"),wxT("BuddyTreeList::buddyStatusChanged removed from subGroup"));
				}
			}else{
				wxLogError(wxT("BuddyTreeList::buddyStatusChanged: (new Status Offline) Buddy %s (%s) not found, newStatus: OFFLINE"),event.getBuddy()->getCustomName().c_str(),event.getBuddy()->getScreenName().c_str());
			}
		//the old Status is an offline status, and the new one is an online status
		}else{
			BuddyTreeItem* bti = offlineBuddies->getChild(event.getBuddy());
			if(bti){
				offlineBuddies->removeChild(bti);
				addBuddyTreeItemToRightGroup(bti);
				wxLogTrace(wxT("BUDDYLIST"),wxT("BuddyTreeList::buddyStatusChanged added to Right Group"));
			}else{
				wxLogError(wxT("BuddyTreeList::buddyStatusChanged: (old Status OFFLINE) Buddy not found (offlineBuddies)"));
			}
		}
	}
	Refresh(false);
}

void BuddyTreeList::onAvatarChanged(AvatarChangedEvent & event){
	event.Skip();
	refreshBuddy(event.getBuddy());
	if(toolTip->getCurBuddy()==event.getBuddy()){
		toolTip->refreshInfo();
	}
}

void BuddyTreeList::onBuddyChanged(BuddyChangedEvent & event){
	event.Skip();
	Buddy* buddy = event.getBuddy();
	switch (event.getChangeType()) {
		case BUDDY_CHANGED_ADDED:
			addBuddy(buddy, buddy->getGroup());
			break;
		case BUDDY_CHANGED_GROUP:
			removeBuddy(buddy);
			addBuddy(buddy,event.getNewGroup());
			break;
		case BUDDY_CHANGED_PRIORITY:
			if(shownPriority != BUDDY_PRIORITY_LOW){
				Refresh(false);
			}else{
				refreshBuddy(buddy);
			}
			break;
		case BUDDY_CHANGED_CUSTOMNAME:
		case BUDDY_CHANGED_PRIVACY_LIST:
		default:
			refreshBuddy(buddy);
			break;
	}
}

void BuddyTreeList::onGroupChanged(GroupChangedEvent & event){
	event.Skip();
	Group* group = event.getGroup();
	switch (event.getChangeType()) {
		case GROUP_CHANGED_ADDED:
			addGroup(group);
			break;
		default:
			break;
	}
}

void BuddyTreeList::onMessageIn(MessageEvent & event){
	event.Skip();
	refreshBuddy(event.getBuddy());
}
void BuddyTreeList::onMessageRead(MessageReadEvent & event){
	event.Skip();
	refreshBuddy(event.getSender());
}


bool BuddyTreeList::shouldDraw(GroupTreeItem* gti){
	return !(hideEmptyGroups && gti->getSize() == 0);
}

bool BuddyTreeList::shouldDraw(BuddyTreeItem* bti){
	switch(shownPriority){
		case BUDDY_PRIORITY_LOW:
			//Allwas show all Buddies
			return true;
		case BUDDY_PRIORITY_NORMAL:
			//only show buddies with normal and high priority
			return bti->getBuddy()->getPriority() != BUDDY_PRIORITY_LOW;
		case BUDDY_PRIORITY_HIGH:
			//only show Buddies with high priority
			return bti->getBuddy()->getPriority() == BUDDY_PRIORITY_HIGH;
		default:
			wxLogDebug(wxT("BuddyTreeList::shoudDraw: Illegal shownPriority setting"));
			shownPriority = BUDDY_PRIORITY_NORMAL;
			return true;
	}
}

bool BuddyTreeList::isVisible(wxCoord & y, wxCoord height){
	if(y==-1){
		return false;
	}

	y = toScrolledPosition(y);
    wxRect rect = GetClientRect();
	return y >= rect.y && y+height < rect.y+rect.height ;
}

wxCoord BuddyTreeList::getPosition(TreeItem* item){
    for(size_t i=0; i <  displayedItems.size(); i++ ){
    	if(displayedItems[i].item==item){
    		return displayedItems[i].pos.y;
    	}
    }
    return -1;
}

wxCoord BuddyTreeList::toScrolledPosition(wxCoord y){
    wxPoint pos = CalcScrolledPosition(wxPoint(0,y));
    return pos.y;
}

void BuddyTreeList::onKeyDown(wxKeyEvent& event){

	switch (event.GetKeyCode()) {
		case WXK_RETURN:
			if(selected!=NULL){
				if(BuddyTreeItem* bi = dynamic_cast<BuddyTreeItem*>(selected)){
					iChatWindow* chatWin = Impp::exemplar()->getModuleManager()->getChatWindowModule();
					if(chatWin != NULL){
						chatWin->showChatWindow(bi->getBuddy());
					}else{
						wxLogDebug(wxT("BuddyTreeList::onKeyDown No Chatwindow that can be showen!"));
					}
				}else if(GroupTreeItem* gi = dynamic_cast<GroupTreeItem*>(selected)){
					setGroupExpended(gi,!gi->isExtended());
				}
			}
			break;
		case WXK_UP:
			if(selected!=NULL){
			    for(size_t i=0; i <  displayedItems.size(); i++ ){
			    	if(displayedItems[i].item==selected){
			    		if(i>1){
			    			wxCoord size = dynamic_cast<GroupTreeItem*>(displayedItems[i-1].item) ? groupSize.y : buddySize.y;
			    			if(!isVisible(displayedItems[i-1].pos.y, size)){
			    				wxCoord scrollSize = size/10;
			    				if(size%10 != 0){
			    					scrollSize++;
			    				}
			    				Scroll(0,GetScrollPos(wxVERTICAL)-scrollSize);
			    			}
			    			Refresh();
			    			setSelected(displayedItems[i-1].item);
			    		}
			    		break;
			    	}
			    }
			}
		break;
		case WXK_DOWN:
			if(selected!=NULL){
			    for(size_t i=0; i <  displayedItems.size(); i++ ){
			    	if(displayedItems[i].item==selected){
			    		if(i<displayedItems.size()-1){
			    			wxCoord size = dynamic_cast<GroupTreeItem*>(displayedItems[i+1].item) ? groupSize.y : buddySize.y;
			    			if(!isVisible(displayedItems[i+1].pos.y, size)){
			    				wxCoord scrollSize = size/10;
			    				if(size%10 != 0){
			    					scrollSize++;
			    				}
			    				Scroll(0,GetScrollPos(wxVERTICAL)+scrollSize);
			    			}
			    			Refresh();
			    			setSelected(displayedItems[i+1].item);
			    		}
			    		break;
			    	}
			    }
			}
		break;
		default:
			event.Skip();
			break;
	}

}

void BuddyTreeList::updateBuddyCustomName(Buddy* buddy){
	refreshBuddy(buddy);
}

// Empty implementation, to prevent flicker
void BuddyTreeList::OnEraseBackground(wxEraseEvent& event) {}

}



/*
GdkRectangle area;
area.x = 0;
area.y = pos.y;
area.height = buddySize.y;
area.width = buddySize.x;


GtkWidget* s = (GtkWidget*) GetHandle();
gtk_paint_flat_box(s->style, dc.GetGDKWindow(),
		GTK_STATE_SELECTED, GTK_SHADOW_NONE,
	       &area, NULL, "treeitem",
	       area.x, area.y, area.width, area.height);
*/

